


#include "base/input_output_context.h"
#include "base_util.h"
#include "codec_config.h"
#include "dump.h"
#include "message_queue.h"
#include "packet_frame_operate.h"
#include "operate_define.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <thread>


struct MessageQueue
{
	PacketMessageQueue packet_message_queue;
	FrameMessageQueue  frame_message_queue;
	PacketMessageQueue video_message_queue;
	PacketMessageQueue audio_message_queue;
};

void ReadPacketDataFromFmtCtx(InputFormatContext & input_fmt_ctx, MessageQueue & message_queue)
{
	AVPacket * packet = av_packet_alloc();
	ReadPacketDataToMessageQueue(&input_fmt_ctx, packet, message_queue.packet_message_queue);
	av_packet_free(&packet);
}

void ProcessPacketData(InputFormatContext & input_fmt_ctx, OutputFormatContext & output_fmt_ctx, MessageQueue & message_queue)
{

	ProcessPacketDataFromMessageQueue(message_queue.packet_message_queue, [&input_fmt_ctx,&output_fmt_ctx, &message_queue](AVPacket * packet){

		if (input_fmt_ctx.VideoMediaStream()->Index() == packet->stream_index)
		{
			DecodePacketDataToMessageQueue(input_fmt_ctx.VideoDecoder(), packet, message_queue.frame_message_queue);
			ProcessFrameDataFromMessageQueue(message_queue.frame_message_queue, [&input_fmt_ctx, &output_fmt_ctx, &message_queue](AVFrame * frame){

				// init encoder ctx hw data
				if (!output_fmt_ctx.VideoMediaStream())
				{
					CodecConfig output_config;

					output_config.m_video_config.m_hw_name = "qsv";
					output_config.m_video_config.m_frame   = frame;
                    output_config.m_video_config.m_copy_decoder_param = true;
					output_fmt_ctx.CreateVideoStream(input_fmt_ctx.VideoMediaStream(), output_config);
					output_fmt_ctx.CreateVideoStreamAndInitHwFrameCtx(input_fmt_ctx.VideoMediaStream());
				}
				(void)input_fmt_ctx;
				EncodeFrameDataToMessageQueue(output_fmt_ctx.VideoEncoder(), frame, message_queue.video_message_queue);

				while (true)
				{
					AVPacket * pkt = nullptr;
					int ret = message_queue.video_message_queue.PopPacketNonBlocking(&pkt);
					if (ret < 0)
					{
						break;
					}
					printf("is video : %d pts %8ld and dts : %8ld.\n", 1, pkt->pts, pkt->dts);
					av_packet_rescale_ts(pkt, output_fmt_ctx.VideoEncoder()->AvCodecCtx()->time_base, output_fmt_ctx.VideoMediaStream()->AvStream()->time_base);
					av_interleaved_write_frame(output_fmt_ctx.AvFormatContext(), pkt);

					av_packet_free(&pkt);
				}

			}, false);
		}
		else
		{

		}
	}, true);
	av_write_trailer(output_fmt_ctx.AvFormatContext());
}

int main(int argc,const char * argv[])
{

	if (argc < 2)
	{
		PrintLog("need input filename param");
		return EXIT_FAILURE;
	}
	std::string infilename = argv[1];
	std::string outfilename = "./1.mp4";
	
	InputFormatContext input_fmt_ctx;
	OutputFormatContext output_fmt_ctx;

	CodecConfig output_config;

	output_config.m_video_config.m_hw_name = "qsv";

	input_fmt_ctx.OpenUrlWithFindStreamInfo(infilename);

	CodecConfig input_config;
	input_config.m_video_config.m_hw_name = "qsv";

	input_fmt_ctx.CreateVideoDecoderCtx("", input_config);
	// input_fmt_ctx.CreateAudioDecoderCtx("", CodecConfig{});

	output_fmt_ctx.CreateUrl(outfilename);

	auto video_media = input_fmt_ctx.VideoMediaStream();
	auto audio_media = input_fmt_ctx.AudioMediaStream();

	// output_fmt_ctx.CreateVideoStream(video_media, output_config);
	// output_fmt_ctx.CreateAudioStream(audio_media, CodecConfig{});
	MessageQueue message_queue;
	message_queue.packet_message_queue.InitMessageQueue(100);
	message_queue.frame_message_queue.InitMessageQueue(100);
	message_queue.video_message_queue.InitMessageQueue(100);
	message_queue.audio_message_queue.InitMessageQueue(100);

	std::thread thd = std::thread([&input_fmt_ctx, &message_queue](){

		ReadPacketDataFromFmtCtx(input_fmt_ctx, message_queue);
	});
	ProcessPacketData(input_fmt_ctx, output_fmt_ctx, message_queue);

	if (thd.joinable())
	{
		thd.join();
	}
	return EXIT_SUCCESS;
}
			